// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package rsa

import (
	"crypto"
	"crypto/subtle"
	"errors"
	"io"
	"math/big"

	"crypto/internal/randutil"
)

// 此文件使用PKCS#1 v1实现加密和解密。5.填充物。

// PKCS1v15DecrypterOpts用于将选项传递给PKCS#1V1。5使用
// 密码解密。解密程序接口。
type PKCS1v15DecryptOptions struct {
	// SessionKeyLen是正在被
	// 解密的会话密钥的长度。如果不是零，则解密过程中的填充错误将导致返回此长度的随机明文，而不是返回错误。这些选择是在固定的时间内发生的。
	SessionKeyLen int
}

// EncryptPKCS1v15使用RSA和填充
// PKCS#1 v1的方案对给定消息进行加密。5.消息长度不能超过
// 公共模长度减去11字节。
// 
// 将rand参数用作熵源，以确保对同一消息进行两次加密不会产生相同的
// 密文。
// 
// 警告：使用此函数加密除
// 会话密钥以外的明文是危险的。在新协议中使用RSA OAEP。
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
	randutil.MaybeReadByte(rand)

	if err := checkPub(pub); err != nil {
		return nil, err
	}
	k := pub.Size()
	if len(msg) > k-11 {
		return nil, ErrMessageTooLong
	}

	// EM=0x00 | | 0x02 | | | PS | | 0x00 | | M 
	em := make([]byte, k)
	em[1] = 2
	ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
	err := nonZeroRandomBytes(ps, rand)
	if err != nil {
		return nil, err
	}
	em[len(em)-len(msg)-1] = 0
	copy(mm, msg)

	m := new(big.Int).SetBytes(em)
	c := encrypt(new(big.Int), pub, m)

	return c.FillBytes(em), nil
}

// DecryptPKCS1v15使用RSA和PKCS#1 v1中的填充方案对明文进行解密。5.
// 如果兰德！=nil，它使用RSA致盲来避免定时侧通道攻击。
// 
// 请注意，无论此函数是否返回错误，都会泄露机密
// 信息。如果攻击者可以使此函数重复运行，并且
// 了解每个实例是否返回错误，则他们可以解密并伪造签名，就像他们拥有私钥一样。请参阅
// DecryptPKCS1v15SessionKey以了解解决此问题的方法。
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) {
	if err := checkPub(&priv.PublicKey); err != nil {
		return nil, err
	}
	valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
	if err != nil {
		return nil, err
	}
	if valid == 0 {
		return nil, ErrDecryption
	}
	return out[index:], nil
}

// DecryptPKCS1v15SessionKey使用RSA和PKCS#1V1中的填充方案对会话密钥进行解密。5.
// 如果兰德！=nil，它使用RSA致盲来避免定时侧通道攻击。
// 如果密文的长度错误，或者如果
// 密文大于公共模数，则返回错误。否则，
// 不会返回任何错误。如果填充有效，则生成的明文消息将被复制到密钥
// 中。否则，密钥不变。这些替代品在恒定的
// /时间内出现。此函数的目的是让用户预先生成一个随机
// 会话密钥，并使用结果值继续协议。
// 这将消除攻击者了解有关明文的任何信息的可能性。
// 参见``针对基于RSA的协议的选定密文攻击
// 加密标准PKCS#1'，Daniel Bleichenbacher，密码学进展
// （Crypto'98）。
// 
// 请注意，如果会话密钥太小，则
// 攻击者可能会对其进行暴力攻击。如果他们能做到这一点，那么他们就可以了解是否使用了随机值（因为同一密文的随机值不同）以及填充是否正确。这违背了
// 函数的要点。使用至少16字节的密钥可以防止这种攻击。
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error {
	if err := checkPub(&priv.PublicKey); err != nil {
		return err
	}
	k := priv.Size()
	if k-(len(key)+3+8) < 0 {
		return ErrDecryption
	}

	valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext)
	if err != nil {
		return err
	}

	if len(em) != k {
		// 这应该是不可能的，因为decryptPKCS1v15始终返回完整的切片。
		return ErrDecryption
	}

	valid &= subtle.ConstantTimeEq(int32(len(em)-index), int32(len(key)))
	subtle.ConstantTimeCopy(valid, key, em[len(em)-len(key):])
	return nil
}

// decryptPKCS1v15使用priv对密文进行解密，如果
// rand不为零，则将该操作置盲。它在valid中返回一或零，指示
// 明文的结构是否正确。在任何一种情况下，明文都是在em中返回的
// 以便可以独立于其是否有效来读取，以保持恒定的内存访问模式。如果明文是
// 有效，则索引包含em中原始消息的索引。
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) {
	k := priv.Size()
	if k < 11 {
		err = ErrDecryption
		return
	}

	c := new(big.Int).SetBytes(ciphertext)
	m, err := decrypt(rand, priv, c)
	if err != nil {
		return
	}

	em = m.FillBytes(make([]byte, k))
	firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
	secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)

	// 明文的其余部分必须是非零随机
	// 八位字节的字符串，后跟0，后跟消息。
	// lookingForIndex:1如果我们仍在寻找零。
	// 索引：第一个零字节的偏移量。
	lookingForIndex := 1

	for i := 2; i < len(em); i++ {
		equals0 := subtle.ConstantTimeByteEq(em[i], 0)
		index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
		lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
	}

	// PS填充长度必须至少为8个字节，并从两个
	// 字节开始进入em。
	validPS := subtle.ConstantTimeLessOrEq(2+8, index)

	valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
	index = subtle.ConstantTimeSelect(valid, index+1, 0)
	return valid, em, index, nil
}

// 非零随机字节用非零随机八位字节填充给定的片。
func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
	_, err = io.ReadFull(rand, s)
	if err != nil {
		return
	}

	for i := 0; i < len(s); i++ {
		for s[i] == 0 {
			_, err = io.ReadFull(rand, s[i:i+1])
			if err != nil {
				return
			}
			// 在测试中，PRNG可能返回全零，因此我们执行
			// 以打破循环。
			s[i] ^= 0x42
		}
	}

	return
}

// 这些是ASN1 DER结构：
// DigestInfo:：=SEQUENCE{
// digestAlgorithm标识符，
// digest八进制字符串
// /}
// 为了性能，我们不使用通用的ASN1编码器。相反，我们要预先计算摘要值的前缀，该前缀将生成一个包含正确内容的有效ASN1 DER字符串
// 。
var hashPrefixes = map[crypto.Hash][]byte{
	crypto.MD5:       {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
	crypto.SHA1:      {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
	crypto.SHA224:    {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
	crypto.SHA256:    {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
	crypto.SHA384:    {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
	crypto.SHA512:    {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
	crypto.MD5SHA1:   {}, // 一个不使用ASN1前缀的特殊TLS案例。
	crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
}

// SignPKCS1v15使用
// RSASSA-PKCS1-V1#u 5-SIGN from RSA PKCS#1 V1计算散列的签名。5.请注意，hash必须是使用给定hash 
// 函数对输入消息进行哈希运算的结果。如果hash为零，则hashed直接签名。除了互操作性，这不是
// 建议的。
// 
// 如果rand不是零，则将使用RSA盲法来避免定时
// 侧通道攻击。这个函数是确定性的。因此，如果可能的
// 消息集很小，攻击者可能能够构建从
// 消息到签名的映射，并识别签名的消息。一如既往，
// 签名提供真实性，而不是保密性。
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
	hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
	if err != nil {
		return nil, err
	}

	tLen := len(prefix) + hashLen
	k := priv.Size()
	if k < tLen+11 {
		return nil, ErrMessageTooLong
	}

	// EM=0x00 | | 0x01 | | | PS | | 0x00 | | T 
	em := make([]byte, k)
	em[1] = 1
	for i := 2; i < k-tLen-1; i++ {
		em[i] = 0xff
	}
	copy(em[k-tLen:k-hashLen], prefix)
	copy(em[k-hashLen:k], hashed)

	m := new(big.Int).SetBytes(em)
	c, err := decryptAndCheck(rand, priv, m)
	if err != nil {
		return nil, err
	}

	return c.FillBytes(em), nil
}

// VerifyPKCS1v15验证RSA PKCS#1 v1。5.签名。hash是使用给定的hash函数对输入消息进行哈希运算的结果，sig是签名。有效签名由
// 指示，返回一个零错误。如果hash为零，则直接使用hash。除了互操作性之外，这个
// 是不可取的。
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
	hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
	if err != nil {
		return err
	}

	tLen := len(prefix) + hashLen
	k := pub.Size()
	if k < tLen+11 {
		return ErrVerification
	}

	// RFC 8017第8.2.2节：如果签名的长度S不是k 
	// 八位字节（其中k是RSA模n的八位字节长度），则输出
	// “无效签名”并停止。
	if k != len(sig) {
		return ErrVerification
	}

	c := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, c)
	em := m.FillBytes(make([]byte, k))
	// EM=0x00 | | 0x01 | | PS | | 0x00 | | T 

	ok := subtle.ConstantTimeByteEq(em[0], 0)
	ok &= subtle.ConstantTimeByteEq(em[1], 1)
	ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed)
	ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix)
	ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0)

	for i := 2; i < k-tLen-1; i++ {
		ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
	}

	if ok != 1 {
		return ErrVerification
	}

	return nil
}

func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) {
	// 特例：加密。散列（0）用于指示数据是
	// 直接签名的。
	if hash == 0 {
		return inLen, nil, nil
	}

	hashLen = hash.Size()
	if inLen != hashLen {
		return 0, nil, errors.New("crypto/rsa: input must be hashed message")
	}
	prefix, ok := hashPrefixes[hash]
	if !ok {
		return 0, nil, errors.New("crypto/rsa: unsupported hash function")
	}
	return
}
